/*
**  Ply.cc - Read ply files
 */

#include "a48.h"

#include <vector>

using std::vector;
using std::multimap;
using namespace std;

namespace A48
{

  Mesh *mesh;
  int v_i;
  Vertex** verts;
  HedgeMap* hedges;
  FaceMap* faces;
  double vertex_coords[3];

  PatchMap* patchMap;
  bool list_faces = false;
  long nFacesPatch = 0;
  bool list_adj_patches = false;
  multimap <int, int> adjPatcesMMap;
  int camera_index;
  float box[14];

  int face_indx[3];
  float coordText[6];
  int *patch_indx;
  int flagProperty;
  bool flagTexture;

  vector<int> grid;
  int ngrid, grid_cnt;
  int range_width, range_height;

  void mkface(int i, int j, int k)
  {
    mesh->put_face(grid[i], grid[j], grid[k], verts, hedges);
  }

  void triangulate_grid()
  {
    int i, j;
    int ntris = 0;

    for (j = 0; j < range_height - 1; j++)
    {
      for (i = 0; i < range_width - 1; i++)
      {
        int ll = i + j * range_width;
        int lr = ll + 1;
        int ul = ll + range_width;
        int ur = ul + 1;
        int nvalid = (grid[ll] >= 0) + (grid[lr] >= 0) +
                     (grid[ul] >= 0) + (grid[ur] >= 0);
        if (nvalid == 4)
          ntris += 2;
        else if (nvalid == 3)
          ntris += 1;
      }
    }
    for (j = 0; j < range_height - 1; j++)
    {
      for (i = 0; i < range_width - 1; i++)
      {
        int ll = i + j * range_width;
        int lr = ll + 1;
        int ul = ll + range_width;
        int ur = ul + 1;
        int nvalid = (grid[ll] >= 0) + (grid[lr] >= 0) +
                     (grid[ul] >= 0) + (grid[ur] >= 0);
        if (nvalid < 3)
          continue;
        if (nvalid == 4)
        {
          // Triangulate in the direction that
          // gives the shorter diagonal
          float ll_ur = norm2(verts[grid[ll]]->a.g - verts[grid[ur]]->a.g);
          float lr_ul = norm2(verts[grid[lr]]->a.g - verts[grid[ul]]->a.g);
          if (ll_ur < lr_ul)
          {
            mkface(ll, lr, ur);
            mkface(ll, ur, ul);
          }
          else
          {
            mkface(ll, lr, ul);
            mkface(lr, ur, ul);
          }
          continue;
        }
        // nvalid == 3
        if (grid[ll] < 0)
          mkface(lr, ur, ul);
        else if (grid[lr] < 0)
          mkface(ll, ur, ul);
        else if (grid[ul] < 0)
          mkface(ll, lr, ur);
        else
          mkface(ll, lr, ul);
      }
    }
  }


  int vertex_cb(p_ply_argument argument)
  {
    double v; long k;

	ply_get_argument_user_data(argument, NULL, &k);
    v = ply_get_argument_value(argument);

    if (k >= 0 && k <= 2)
      vertex_coords[k] = v;
    if (k==2)
    {
      verts[v_i] = new Vertex();
      verts[v_i]->a = Point(vertex_coords[0], vertex_coords[1], vertex_coords[2]);
      mesh->put_vertex(verts[v_i]);
      v_i++;
    }
    return 1;
  }


  int face_cb(p_ply_argument argument)
  {
    float i; long length, k;

    ply_get_argument_property(argument, NULL, &length, &k);
    i =  (float) ply_get_argument_value(argument);

    if ((k == -1) && ((int)i == 3)) flagProperty = 0;
    if ((k == -1) && ((int)i == 6)) flagProperty = 1;

    if ((flagProperty == 0) && (k >= 0 && k <= 2)) face_indx[k] = (int)i;
    if ((flagProperty == 1) && (k >= 0 && k <= 5)) coordText[k] = i;

	if (flagTexture == false) {
		if ((flagProperty == 0) && (k == 2))
		{
			mesh->put_face(face_indx[0], face_indx[1], face_indx[2], verts, hedges);
		}
	}
	else
	{
		if ((flagProperty == 1) && (k == 5))
		{
			mesh->put_face(face_indx[0], face_indx[1], face_indx[2], coordText[0], coordText[1], coordText[2], coordText[3], coordText[4], coordText[5], verts, hedges);
		}
	}

    return 1;
  }

  int patch_cb(p_ply_argument argument)
  {
    double v; int i; long length, k;
	int n_properties = 15;

	if ((list_faces == true) || (list_adj_patches == true))
	{
        ply_get_argument_property(argument, NULL, &length, &k);
        i =  (int) ply_get_argument_value(argument);
		
		if (list_faces == true)
		{
			if (k == -1)
			{
			  patch_indx = (int *) malloc(length * sizeof(int));
			}

			if (k >= 0)
			{
			  if (i >= mesh->num_faces())
			  {
				  fprintf(stderr, "Patch inconsistency: face index %d is bigger than the mesh size (%d).", i, mesh->num_faces());

				  patch_indx[k] = 0;
			  }
			  else
			  {
				  patch_indx[k] = i;
			  }

			}

			if (k == (length - 1))
			{
			  nFacesPatch = length;

			  list_faces = false;
			  list_adj_patches = true;
			}
		}
		else
		{
			if (k >= 0)
			{
				adjPatcesMMap.insert(pair<int, int>(mesh->num_patches(), i));
			}

			if (k == (length - 1))
			{
			  mesh->put_patch(patch_indx, nFacesPatch, faces, camera_index, box);

			  free(patch_indx);

			  list_faces = false;
			  list_adj_patches = false;
			}
		}
	}
	else
	{
        ply_get_argument_user_data(argument, NULL, &k);
        v = ply_get_argument_value(argument);

		if (k == 0)
		{
			camera_index = (int)v;
		}
		else
		{
			box[k-1] = v;
		}

		if (k == (n_properties - 1))
		{
			list_faces = true;
		}
	}

    return 1;
  }

  int range_cb(p_ply_argument argument)
  {
    long length, value_index;

    ply_get_argument_property(argument, NULL, &length, &value_index);
    if (value_index >= 0 || length == 0)
    {
      int arg_val = (int) ply_get_argument_value(argument);
      if (length == 0)
        grid[grid_cnt] = -1;
      else
        grid[grid_cnt] = arg_val;
      grid_cnt++;
    }
    return 1;
  }


  void read_obj_info(p_ply ply)
  {
    const char *value = NULL;
	
	while ((value = ply_get_next_comment(ply, value)))
	{
		char name[1024];
		
		if (sscanf(value, "texturefile %s", name))
			flagTexture = true;
	}

    while ((value = ply_get_next_obj_info(ply, value)))
    {
      int n;
      if (sscanf(value, "num_cols %d", &n))
        sscanf(value, "num_cols %d", &range_width);
      if (sscanf(value, "num_rows %d", &n))
        sscanf(value, "num_rows %d", &range_height);
    }
    ngrid = range_width*range_height;
    grid_cnt = 0;
  }

  void erro(const char *msg)
  {
    printf("RPLY error: %s\n", msg);
  }

  int ply_read_file(char *fname, Mesh *m)
  {
    int rc; long nv, nt, nr;

    flagProperty = 0;

    p_ply ply = ply_open(fname, erro);
    if (!ply)
      return 1;
    if (!ply_read_header(ply))
      return 1;
    read_obj_info(ply);

    mesh = m;
    nv = ply_set_read_cb(ply, "vertex", "x", vertex_cb, NULL, 0);
    ply_set_read_cb(ply, "vertex", "y", vertex_cb, NULL, 1);
    ply_set_read_cb(ply, "vertex", "z", vertex_cb, NULL, 2);
    nt = ply_set_read_cb(ply, "face", "vertex_indices", face_cb, NULL, 0);
         ply_set_read_cb(ply, "face", "texcoord", face_cb, NULL, 1);
    //np = ply_set_read_cb(ply, "patch", "face_indices", patch_cb, NULL, 0);
    nr = ply_set_read_cb(ply, "range_grid", "vertex_indices", range_cb, NULL, 0);

    v_i = 0;
    verts = new Vertex*[nv];
    hedges = new HedgeMap();
    grid.resize(ngrid, -1);

    if (!(rc=ply_read(ply)))
    {
      ply_close(ply);
    }
    if (ngrid > 0)
      triangulate_grid();

    grid.clear();
    delete(verts);
    delete(hedges);
    return rc;
  }

  int ply_read_patches_file(char *fname, Mesh *m)
  {
    int rc;

    flagProperty = 0;

    p_ply ply = ply_open(fname, erro);
    if (!ply)
      return 1;
    if (!ply_read_header(ply))
      return 1;
    read_obj_info(ply);

    mesh = m;
     ply_set_read_cb(ply, "patch", "camera_index", patch_cb, NULL, 0);

     ply_set_read_cb(ply, "patch", "box_3d_min_x", patch_cb, NULL, 1);
	 ply_set_read_cb(ply, "patch", "box_3d_min_y", patch_cb, NULL, 2);
	 ply_set_read_cb(ply, "patch", "box_3d_min_z", patch_cb, NULL, 3);
     ply_set_read_cb(ply, "patch", "box_3d_max_x", patch_cb, NULL, 4);
	 ply_set_read_cb(ply, "patch", "box_3d_max_y", patch_cb, NULL, 5);
	 ply_set_read_cb(ply, "patch", "box_3d_max_z", patch_cb, NULL, 6);

     ply_set_read_cb(ply, "patch", "box_2d_img_min_x", patch_cb, NULL, 7);
	 ply_set_read_cb(ply, "patch", "box_2d_img_min_y", patch_cb, NULL, 8);
     ply_set_read_cb(ply, "patch", "box_2d_img_max_x", patch_cb, NULL, 9);
	 ply_set_read_cb(ply, "patch", "box_2d_img_max_y", patch_cb, NULL, 10);

     ply_set_read_cb(ply, "patch", "box_2d_text_min_x", patch_cb, NULL, 11);
	 ply_set_read_cb(ply, "patch", "box_2d_text_min_y", patch_cb, NULL, 12);
     ply_set_read_cb(ply, "patch", "box_2d_text_max_x", patch_cb, NULL, 13);
	 ply_set_read_cb(ply, "patch", "box_2d_text_max_y", patch_cb, NULL, 14);

     ply_set_read_cb(ply, "patch", "face_indices", patch_cb, NULL, 15);

     ply_set_read_cb(ply, "patch", "adj_patch_indices", patch_cb, NULL, 16);

	faces = new FaceMap();

	for (FaceIter f = mesh->faces_begin(); f != mesh->faces_end(); f++)
    {
		(*faces)[(*f)->get_index()] = (*f);
	}

    if (!(rc=ply_read(ply)))
    {
      ply_close(ply);
    }

    delete(faces);

	//Creating de patch adjacency from the multimap

	patchMap = new PatchMap();
	multimap <int, int>::iterator itr;

	for (PatchIter p = mesh->patches_begin(); p != mesh->patches_end(); p++)
    {
		(*patchMap)[(*p)->get_index()] = (*p);
	}

	for (int i = 0; i < mesh->num_patches(); i++)
    {
	  Patch *patch = (*patchMap->find(i)).second;

	  for (itr = adjPatcesMMap.lower_bound(i); itr != adjPatcesMMap.upper_bound(i); itr++)
	  {
		  Patch *patchAdj = (*patchMap->find((*itr).second)).second;

		  patch->add_adj_patch(patchAdj);
	  }
	}

    return rc;
  }
}
